package com.mall.auth.controller;

import com.mall.auth.entity.OAuth2Provider;
import com.mall.auth.entity.OAuth2User;
import com.mall.auth.service.OAuth2Service;
import com.mall.common.entity.User;
import com.mall.common.result.Result;
import com.mall.common.util.JwtUtil;
import com.mall.common.util.RedisUtil;
import com.mall.system.feign.UserServiceFeignClient;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * OAuth2.0 控制器
 * 处理第三方登录授权相关操作
 */
@Tag(name = "OAuth2.0接口")
@Slf4j
@RestController
@RequestMapping("/auth/oauth2")
public class OAuth2Controller {

    @Autowired
    private OAuth2Service oAuth2Service;

    @Autowired
    private UserServiceFeignClient userServiceFeignClient;

    @Autowired
    private RedisUtil redisUtil;

    @Value("${jwt.secret:mall-secret}")
    private String jwtSecret;

    @Value("${jwt.expiration:3600000}")
    private Long jwtExpiration;

    @Value("${oauth2.wechat.redirect-uri}")
    private String wechatRedirectUri;

    @Value("${oauth2.alipay.redirect-uri}")
    private String alipayRedirectUri;

    @Value("${oauth2.douyin.redirect-uri}")
    private String douyinRedirectUri;

    @Value("${oauth2.qq.redirect-uri}")
    private String qqRedirectUri;

    @Operation(summary = "获取授权URL")
    @GetMapping("/authorize/{provider}")
    public Result<String> authorize(
            @PathVariable("provider") String provider,
            @RequestParam(value = "redirect_uri", required = false) String redirectUri) {
        try {
            OAuth2Provider providerEnum = OAuth2Provider.fromCode(provider);
            if (redirectUri == null || redirectUri.isEmpty()) {
                redirectUri = getDefaultRedirectUri(providerEnum);
            }
            String authorizationUrl = oAuth2Service.getAuthorizationUrl(providerEnum, redirectUri);
            return Result.success(authorizationUrl);
        } catch (Exception e) {
            log.error("获取授权URL异常: ", e);
            return Result.failed("获取授权URL失败: " + e.getMessage());
        }
    }

    @Operation(summary = "OAuth2.0回调处理")
    @GetMapping("/callback/{provider}")
    public Result<Object> callback(
            @PathVariable("provider") String provider,
            @RequestParam("code") String code,
            @RequestParam(value = "state", required = false) String state,
            @RequestParam(value = "error", required = false) String error,
            @RequestParam(value = "error_description", required = false) String errorDescription) {
        try {
            // 处理用户取消授权的情况
            if (error != null && !error.isEmpty()) {
                log.warn("用户取消授权或授权失败: provider={}, error={}, description={}", provider, error, errorDescription);
                // 如果是二维码登录，更新二维码状态为取消
                if (state != null && !state.isEmpty()) {
                    updateQRCodeStatus(state, "cancelled", "用户取消授权: " + errorDescription);
                }
                return Result.failed("用户取消授权: " + errorDescription);
            }
            
            OAuth2Provider providerEnum = OAuth2Provider.fromCode(provider);

            // 获取访问令牌
            String accessToken = oAuth2Service.getAccessToken(providerEnum, code, getDefaultRedirectUri(providerEnum));

            // 获取用户信息
            OAuth2User oAuth2User = oAuth2Service.getUserInfo(providerEnum, accessToken);

            // 检查该第三方账号是否已绑定用户
            String uniqueId = provider + "_" + oAuth2User.getProviderId();
            Result<User> userResult = userServiceFeignClient.getUserByUniqueId(uniqueId);

            User user = null;
            User bindingUser = null;
            
            if (userResult.getCode().equals(200) && userResult.getData() != null) {
                // 该第三方账号已绑定用户
                user = userResult.getData();
                
                // 更新用户信息（如果需要）
                user.setNickname(oAuth2User.getNickname());
                user.setAvatar(oAuth2User.getAvatar());
                userServiceFeignClient.updateUser(user);
            } else {
                // 该第三方账号未绑定用户，检查是否在进行账号绑定操作
                if (state != null && !state.isEmpty()) {
                    // 检查是否是账号绑定操作
                    Object bindingUserInfo = redisUtil.get("user:binding:" + state);
                    if (bindingUserInfo != null) {
                        Map<String, Object> bindingInfo = (Map<String, Object>) bindingUserInfo;
                        Long userId = (Long) bindingInfo.get("userId");
                        Result<User> bindingUserResult = userServiceFeignClient.getUserById(userId);
                        if (bindingUserResult.getCode().equals(200) && bindingUserResult.getData() != null) {
                            bindingUser = bindingUserResult.getData();
                            // 检查该第三方账号是否已经被其他用户绑定
                            Result<User> existingUserResult = userServiceFeignClient.getUserByUniqueId(uniqueId);
                            if (existingUserResult.getCode().equals(200) && existingUserResult.getData() != null) {
                                return Result.failed("该第三方账号已被其他用户绑定");
                            }
                            
                            // 绑定第三方账号到当前用户
                            bindingUser.setThirdPartyId(uniqueId);
                            bindingUser.setNickname(oAuth2User.getNickname());
                            bindingUser.setAvatar(oAuth2User.getAvatar());
                            userServiceFeignClient.updateUser(bindingUser);
                            
                            // 删除绑定信息
                            redisUtil.del("user:binding:" + state);
                            
                            user = bindingUser;
                        }
                    }
                }
                
                // 如果不是绑定操作，创建新用户
                if (bindingUser == null) {
                    user = new User();
                    user.setUsername(generateUsername(provider, oAuth2User.getProviderId())); // 生成用户名
                    user.setNickname(oAuth2User.getNickname());
                    user.setPassword(UUID.randomUUID().toString()); // 随机密码，因为是第三方登录
                    user.setStatus(1); // 默认启用
                    user.setAvatar(oAuth2User.getAvatar());
                    user.setThirdPartyId(uniqueId); // 第三方平台唯一ID

                    // 调用系统服务创建用户
                    Result<User> createUserResult = userServiceFeignClient.createUser(user);
                    if (!createUserResult.getCode().equals(200) || createUserResult.getData() == null) {
                        log.error("创建用户失败: {}", createUserResult.getMessage());
                        return Result.failed("创建用户失败: " + createUserResult.getMessage());
                    }
                    user = createUserResult.getData();
                }
            }

            // 如果是二维码登录，更新二维码状态为已确认，并存储用户信息
            if (state != null && !state.isEmpty()) {
                updateQRCodeStatus(state, "confirmed", user);
            }

            // 生成JWT Token
            String token = generateToken(user);
            Map<String, Object> tokenResult = new HashMap<>(3);
            tokenResult.put("token", token);
            tokenResult.put("tokenType", "Bearer");
            tokenResult.put("user", user);

            return Result.success(tokenResult);
        } catch (Exception e) {
            log.error("OAuth2.0回调处理异常: ", e);
            // 如果是二维码登录，更新二维码状态为失败
            if (state != null && !state.isEmpty()) {
                updateQRCodeStatus(state, "failed", e.getMessage());
            }
            return Result.failed("OAuth2.0回调处理失败: " + e.getMessage());
        }
    }

    @Operation(summary = "刷新访问令牌")
    @PostMapping("/refresh/{provider}")
    public Result<Object> refreshToken(
            @PathVariable("provider") String provider,
            @RequestParam("refreshToken") String refreshToken) {
        try {
            OAuth2Provider providerEnum = OAuth2Provider.fromCode(provider);
            
            // 刷新访问令牌
            OAuth2User oAuth2User = oAuth2Service.refreshAccessToken(providerEnum, refreshToken);
            
            // 生成新的JWT Token
            String uniqueId = provider + "_" + oAuth2User.getProviderId();
            Result<User> userResult = userServiceFeignClient.getUserByUniqueId(uniqueId);
            
            if (!userResult.getCode().equals(200) || userResult.getData() == null) {
                return Result.failed("用户信息不存在");
            }
            
            User user = userResult.getData();
            
            // 更新用户信息
            user.setNickname(oAuth2User.getNickname());
            user.setAvatar(oAuth2User.getAvatar());
            userServiceFeignClient.updateUser(user);
            
            // 生成新的JWT Token
            String token = generateToken(user);
            Map<String, Object> tokenResult = new HashMap<>(3);
            tokenResult.put("token", token);
            tokenResult.put("tokenType", "Bearer");
            tokenResult.put("user", user);
            
            return Result.success(tokenResult);
        } catch (Exception e) {
            log.error("刷新访问令牌异常: ", e);
            return Result.failed("刷新访问令牌失败: " + e.getMessage());
        }
    }
    
    /**
     * 生成用户名
     * 
     * @param provider 第三方提供商
     * @param providerId 第三方平台用户ID
     * @return 生成的用户名
     */
    private String generateUsername(String provider, String providerId) {
        // 使用"平台名_第三方用户ID"格式生成用户名
        return provider + "_" + providerId;
    }

    /**
     * 更新二维码状态
     * 
     * @param state 二维码状态标识
     * @param status 状态 (scanned, confirmed, failed)
     * @param data 相关数据
     */
    private void updateQRCodeStatus(String state, String status, Object data) {
        try {
            Object qrInfoObj = redisUtil.get("qr:info:" + state);
            if (qrInfoObj != null) {
                Map<String, Object> qrInfo = (Map<String, Object>) qrInfoObj;
                qrInfo.put("status", status);
                
                switch (status) {
                    case "scanned":
                        qrInfo.put("scanTime", System.currentTimeMillis());
                        break;
                    case "confirmed":
                        qrInfo.put("confirmTime", System.currentTimeMillis());
                        // 存储用户信息
                        redisUtil.set("qr:user:" + state, data, 300);
                        break;
                    case "failed":
                        qrInfo.put("failTime", System.currentTimeMillis());
                        // 存储错误信息
                        redisUtil.set("qr:error:" + state, data, 300);
                        break;
                    case "cancelled":
                        qrInfo.put("cancelTime", System.currentTimeMillis());
                        // 存储取消信息
                        redisUtil.set("qr:error:" + state, data, 300);
                        break;
                }
                
                // 更新二维码信息
                redisUtil.set("qr:info:" + state, qrInfo, 300);
            }
        } catch (Exception e) {
            log.error("更新二维码状态异常: ", e);
        }
    }

    /**
     * 生成JWT Token
     *
     * @param user 用户信息
     * @return Token
     */
    private String generateToken(User user) {
        Map<String, Object> claims = new HashMap<>(3);
        claims.put("userid", user.getUserId());
        claims.put("username", user.getUsername());
        claims.put("nickname", user.getNickname());

        // 使用JWT工具类生成Token
        return JwtUtil.generateToken(claims, jwtSecret, jwtExpiration);
    }

    /**
     * 获取默认回调地址
     *
     * @param provider 第三方提供商
     * @return 默认回调地址
     */
    private String getDefaultRedirectUri(OAuth2Provider provider) {
        switch (provider) {
            case WECHAT:
                return wechatRedirectUri;
            case ALIPAY:
                return alipayRedirectUri;
            case DOUYIN:
                return douyinRedirectUri;
            case QQ:
                return qqRedirectUri;
            default:
                throw new IllegalArgumentException("不支持的OAuth2提供商: " + provider);
        }
    }
}